package com.youlin.untils;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.beans.BeanUtils;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

public class BaseService {

    /**
     * 查询并处理一对一关系
     * @param parentList
     * @param getParentKey
     * @param parentResClass
     * @param setSub
     * @param iService
     * @param getsubKey
     * @param subResClass
     * @param <K>
     * @param <P>
     * @param <PR>
     * @param <S>
     * @param <SR>
     * @return
     */
    public static <K, P, PR, S, SR> List<PR> findAndDealWithOneToOne(List<P> parentList,
                Function<P, K> getParentKey, Class<PR> parentResClass, SetFunction<PR, SR> setSub,
                IService<S> iService, SFunction<S, K> getsubKey, Class<SR> subResClass) {
        List<K> ids = parentList.parallelStream().map(getParentKey).collect(Collectors.toList());
        if (ids.size() == 0) return parentList.parallelStream().map(p -> {
            if (p.getClass() == parentResClass) return (PR) p;
            return copyProperties(p, parentResClass);
        }).collect(Collectors.toList());

        Class implClass = (Class) iService.getClass().getGenericSuperclass();
        ParameterizedType serviceType = (ParameterizedType) implClass.getGenericSuperclass();
        Class<S> entityClass = (Class) serviceType.getActualTypeArguments()[1];
        List<S> subList = iService.list(Wrappers.lambdaQuery(entityClass).in(getsubKey, ids));
        return dealWithOneToOne(parentList, getParentKey, parentResClass, setSub, subList, getsubKey, subResClass);
    }

    /**
     * 处理一对一关系
     * @param parentList
     * @param getParentKey
     * @param parentResClass
     * @param setSub
     * @param subList
     * @param subResClass
     * @param <P>
     * @param <PR>
     * @param <S>
     * @param <SR>
     * @return
     */
    public static <K, P, PR, S, SR> List<PR> dealWithOneToOne(List<P> parentList,
            Function<P, K> getParentKey, Class<PR> parentResClass, SetFunction<PR, SR> setSub,
            List<S> subList, Function<S, K> getsubKey, Class<SR> subResClass) {
        Map<K, SR> subResMap = subList.parallelStream().collect(Collectors.toMap(getsubKey, sub -> copyProperties(sub, subResClass)));
        return parentSetSub(parentList, getParentKey, parentResClass, setSub, subResMap);
    }

    private static <P, PR, SR> PR setInvoke(P parent, Class<PR> parentResClass, SetFunction<PR, SR> setSub, SR sub) {
        if (parent.getClass() != parentResClass) {
            PR parentResData = copyProperties(parent, parentResClass);
            return setSub.accept(parentResData, sub);
        } else return setSub.accept((PR) parent, sub);
    }

    private static <P, PK, PR, SR> List<PR> parentSetSub (List<P> parentList, Function<P, PK> getParentKey,
                              Class<PR> parentResClass, SetFunction<PR, SR> setSub, Map<PK, SR> subResMap) {
        List<PR> parentResList = parentList.parallelStream().map(parent -> {
            PK pk = getParentKey.apply(parent);
            return setInvoke(parent, parentResClass, setSub, subResMap.get(pk));
        }).collect(Collectors.toList());
        return parentResList;
    }

    public static <E, R> R copyProperties (E instance, Class<R> resClass) {
        try {
            if (instance.getClass() != resClass) {
                R subResData = resClass.newInstance();
                BeanUtils.copyProperties(instance, subResData);
                return subResData;
            } else return (R) instance;
        } catch (Exception e) {
            return null;
        }
    }

}
